home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / components / nsProgressDialog.js < prev    next >
Text File  |  2007-10-18  |  37KB  |  950 lines

  1. /* vim:set ts=4 sts=4 sw=4 et cin: */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Mozilla Progress Dialog.
  16.  *
  17.  * The Initial Developer of the Original Code is
  18.  * Netscape Communications Corp.
  19.  * Portions created by the Initial Developer are Copyright (C) 2002
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Bill Law       <law@netscape.com>
  24.  *   Aaron Kaluszka <ask@swva.net>
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the MPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the MPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK ***** */
  39.  
  40. /* This file implements the nsIProgressDialog interface.  See nsIProgressDialog.idl
  41.  *
  42.  * The implementation consists of a JavaScript "class" named nsProgressDialog,
  43.  * comprised of:
  44.  *   - a JS constructor function
  45.  *   - a prototype providing all the interface methods and implementation stuff
  46.  *
  47.  * In addition, this file implements an nsIModule object that registers the
  48.  * nsProgressDialog component.
  49.  */
  50.  
  51. /* ctor
  52.  */
  53. function nsProgressDialog() {
  54.     // Initialize data properties.
  55.     this.mParent      = null;
  56.     this.mOperation   = null;
  57.     this.mStartTime   = ( new Date() ).getTime();
  58.     this.observer     = null;
  59.     this.mLastUpdate  = Number.MIN_VALUE; // To ensure first onProgress causes update.
  60.     this.mInterval    = 750; // Default to .75 seconds.
  61.     this.mElapsed     = 0;
  62.     this.mLoaded      = false;
  63.     this.fields       = new Array;
  64.     this.strings      = new Array;
  65.     this.mSource      = null;
  66.     this.mTarget      = null;
  67.     this.mTargetFile  = null;
  68.     this.mMIMEInfo    = null;
  69.     this.mDialog      = null;
  70.     this.mDisplayName = null;
  71.     this.mPaused      = false;
  72.     this.mRequest     = null;
  73.     this.mCompleted   = false;
  74.     this.mMode        = "normal";
  75.     this.mPercent     = -1;
  76.     this.mRate        = 0;
  77.     this.mBundle      = null;
  78.     this.mCancelDownloadOnClose = true;
  79. }
  80.  
  81. const nsIProgressDialog      = Components.interfaces.nsIProgressDialog;
  82. const nsIWindowWatcher       = Components.interfaces.nsIWindowWatcher;
  83. const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  84. const nsITextToSubURI        = Components.interfaces.nsITextToSubURI;
  85. const nsIChannel             = Components.interfaces.nsIChannel;
  86. const nsIFileURL             = Components.interfaces.nsIFileURL;
  87. const nsIURL                 = Components.interfaces.nsIURL;
  88. const nsILocalFile           = Components.interfaces.nsILocalFile;
  89.  
  90. nsProgressDialog.prototype = {
  91.     // Turn this on to get debugging messages.
  92.     debug: false,
  93.  
  94.     // Chrome-related constants.
  95.     dialogChrome:   "chrome://global/content/nsProgressDialog.xul",
  96.     dialogFeatures: "chrome,titlebar,minimizable=yes,dialog=no",
  97.  
  98.     // getters/setters
  99.     get saving()            { return this.MIMEInfo == null ||
  100.                               this.MIMEInfo.preferredAction == Components.interfaces.nsIMIMEInfo.saveToDisk; },
  101.     get parent()            { return this.mParent; },
  102.     set parent(newval)      { return this.mParent = newval; },
  103.     get operation()         { return this.mOperation; },
  104.     set operation(newval)   { return this.mOperation = newval; },
  105.     get observer()          { return this.mObserver; },
  106.     set observer(newval)    { return this.mObserver = newval; },
  107.     get startTime()         { return this.mStartTime; },
  108.     set startTime(newval)   { return this.mStartTime = newval/1000; }, // PR_Now() is in microseconds, so we convert.
  109.     get lastUpdate()        { return this.mLastUpdate; },
  110.     set lastUpdate(newval)  { return this.mLastUpdate = newval; },
  111.     get interval()          { return this.mInterval; },
  112.     set interval(newval)    { return this.mInterval = newval; },
  113.     get elapsed()           { return this.mElapsed; },
  114.     set elapsed(newval)     { return this.mElapsed = newval; },
  115.     get loaded()            { return this.mLoaded; },
  116.     set loaded(newval)      { return this.mLoaded = newval; },
  117.     get source()            { return this.mSource; },
  118.     set source(newval)      { return this.mSource = newval; },
  119.     get target()            { return this.mTarget; },
  120.     get targetFile()        { return this.mTargetFile; },
  121.     get MIMEInfo()          { return this.mMIMEInfo; },
  122.     set MIMEInfo(newval)    { return this.mMIMEInfo = newval; },
  123.     get dialog()            { return this.mDialog; },
  124.     set dialog(newval)      { return this.mDialog = newval; },
  125.     get displayName()       { return this.mDisplayName; },
  126.     set displayName(newval) { return this.mDisplayName = newval; },
  127.     get paused()            { return this.mPaused; },
  128.     get completed()         { return this.mCompleted; },
  129.     get mode()              { return this.mMode; },
  130.     get percent()           { return this.mPercent; },
  131.     get rate()              { return this.mRate; },
  132.     get kRate()             { return this.mRate / 1024; },
  133.     get cancelDownloadOnClose() { return this.mCancelDownloadOnClose; },
  134.     set cancelDownloadOnClose(newval) { return this.mCancelDownloadOnClose = newval; },
  135.  
  136.     set target(newval) {
  137.         // If newval references a file on the local filesystem, then grab a
  138.         // reference to its corresponding nsIFile.
  139.         if (newval instanceof nsIFileURL && newval.file instanceof nsILocalFile) {
  140.             this.mTargetFile = newval.file.QueryInterface(nsILocalFile);
  141.         } else {
  142.             this.mTargetFile = null;
  143.         }
  144.  
  145.         return this.mTarget = newval;
  146.     },
  147.  
  148.     // These setters use functions that update the dialog.
  149.     set paused(newval)      { return this.setPaused(newval); },
  150.     set completed(newval)   { return this.setCompleted(newval); },
  151.     set mode(newval)        { return this.setMode(newval); },
  152.     set percent(newval)     { return this.setPercent(newval); },
  153.     set rate(newval)        { return this.setRate(newval); },
  154.  
  155.     // ---------- nsIProgressDialog methods ----------
  156.  
  157.     // open: Store aParentWindow and open the dialog.
  158.     open: function( aParentWindow ) {
  159.         // Save parent and "persist" operation.
  160.         this.parent    = aParentWindow;
  161.  
  162.         // Open dialog using the WindowWatcher service.
  163.         var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  164.                    .getService( nsIWindowWatcher );
  165.         this.dialog = ww.openWindow( this.parent,
  166.                                      this.dialogChrome,
  167.                                      null,
  168.                                      this.dialogFeatures,
  169.                                      this );
  170.     },
  171.  
  172.     init: function( aSource, aTarget, aDisplayName, aMIMEInfo, aStartTime,
  173.                     aTempFile, aOperation ) {
  174.       this.source = aSource;
  175.       this.target = aTarget;
  176.       this.displayName = aDisplayName;
  177.       this.MIMEInfo = aMIMEInfo;
  178.       if ( aStartTime ) {
  179.           this.startTime = aStartTime;
  180.       }
  181.       this.operation = aOperation;
  182.     },
  183.  
  184.     // ---------- nsIWebProgressListener methods ----------
  185.  
  186.     // Look for STATE_STOP and update dialog to indicate completion when it happens.
  187.     onStateChange: function( aWebProgress, aRequest, aStateFlags, aStatus ) {
  188.         if ( aStateFlags & nsIWebProgressListener.STATE_STOP ) {
  189.             // if we are downloading, then just wait for the first STATE_STOP
  190.             if ( this.targetFile != null ) {
  191.                 // we are done transferring...
  192.                 this.completed = true;
  193.                 return;
  194.             }
  195.  
  196.             // otherwise, wait for STATE_STOP with aRequest corresponding to
  197.             // our target.  XXX redirects might screw up this logic.
  198.             try {
  199.                 var chan = aRequest.QueryInterface(nsIChannel);
  200.                 if (chan.URI.equals(this.target)) {
  201.                     // we are done transferring...
  202.                     this.completed = true;
  203.                 }
  204.             }
  205.             catch (e) {
  206.             }
  207.         }
  208.     },
  209.  
  210.     // Handle progress notifications.
  211.     onProgressChange: function( aWebProgress,
  212.                                 aRequest,
  213.                                 aCurSelfProgress,
  214.                                 aMaxSelfProgress,
  215.                                 aCurTotalProgress,
  216.                                 aMaxTotalProgress ) {
  217.       return onProgressChange64(aWebProgress, aRequest, aCurSelfProgress,
  218.               aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress);
  219.     },
  220.  
  221.     onProgressChange64: function( aWebProgress,
  222.                                   aRequest,
  223.                                   aCurSelfProgress,
  224.                                   aMaxSelfProgress,
  225.                                   aCurTotalProgress,
  226.                                   aMaxTotalProgress ) {
  227.         var overallProgress = aCurTotalProgress;
  228.  
  229.         // Get current time.
  230.         var now = ( new Date() ).getTime();
  231.  
  232.         // If interval hasn't elapsed, ignore it.
  233.         if ( now - this.lastUpdate < this.interval &&
  234.              aMaxTotalProgress != "-1" &&
  235.              parseInt( aCurTotalProgress ) < parseInt( aMaxTotalProgress ) ) {
  236.             return;
  237.         }
  238.  
  239.         // Update this time.
  240.         this.lastUpdate = now;
  241.  
  242.         // Update elapsed time.
  243.         this.elapsed = now - this.startTime;
  244.  
  245.         // Calculate percentage.
  246.         if ( aMaxTotalProgress > 0) {
  247.             this.percent = Math.floor( ( overallProgress * 100.0 ) / aMaxTotalProgress );
  248.         } else {
  249.             this.percent = -1;
  250.         }
  251.  
  252.         // If dialog not loaded, then don't bother trying to update display.
  253.         if ( !this.loaded ) {
  254.             return;
  255.         }
  256.  
  257.         // Update dialog's display of elapsed time.
  258.         this.setValue( "timeElapsed", this.formatSeconds( this.elapsed / 1000 ) );
  259.  
  260.         // Now that we've set the progress and the time, update # bytes downloaded...
  261.         // Update status (nnK of mmK bytes at xx.xK aCurTotalProgress/sec)
  262.         var status = this.getString( "progressMsg" );
  263.  
  264.         // Insert 1 is the number of kilobytes downloaded so far.
  265.         status = this.replaceInsert( status, 1, parseInt( overallProgress/1024 + .5 ) );
  266.  
  267.         // Insert 2 is the total number of kilobytes to be downloaded (if known).
  268.         if ( aMaxTotalProgress != "-1" ) {
  269.             status = this.replaceInsert( status, 2, parseInt( aMaxTotalProgress/1024 + .5 ) );
  270.         } else {
  271.             status = this.replaceInsert( status, 2, "??" );
  272.         }
  273.  
  274.         // Insert 3 is the download rate.
  275.         if ( this.elapsed ) {
  276.             this.rate = ( aCurTotalProgress * 1000 ) / this.elapsed;
  277.             status = this.replaceInsert( status, 3, this.rateToKRate( this.rate ) );
  278.         } else {
  279.             // Rate not established, yet.
  280.             status = this.replaceInsert( status, 3, "??.?" );
  281.         }
  282.  
  283.         // All 3 inserts are taken care of, now update status msg.
  284.         this.setValue( "status", status );
  285.  
  286.         // Update time remaining.
  287.         if ( this.rate && ( aMaxTotalProgress > 0 ) ) {
  288.             // Calculate how much time to download remaining at this rate.
  289.             var rem = Math.round( ( aMaxTotalProgress - aCurTotalProgress ) / this.rate );
  290.             this.setValue( "timeLeft", this.formatSeconds( rem ) );
  291.         } else {
  292.             // We don't know how much time remains.
  293.             this.setValue( "timeLeft", this.getString( "unknownTime" ) );
  294.         }
  295.     },
  296.  
  297.     // Look for error notifications and display alert to user.
  298.     onStatusChange: function( aWebProgress, aRequest, aStatus, aMessage ) {
  299.         // Check for error condition (only if dialog is still open).
  300.         if ( aStatus != Components.results.NS_OK ) {
  301.             if ( this.loaded ) {
  302.                 // Get prompt service.
  303.                 var prompter = Components.classes[ "@mozilla.org/embedcomp/prompt-service;1" ]
  304.                                    .getService( Components.interfaces.nsIPromptService );
  305.                 // Display error alert (using text supplied by back-end).
  306.                 var title = this.getProperty( this.saving ? "savingAlertTitle" : "openingAlertTitle",
  307.                                               [ this.fileName() ],
  308.                                               1 );
  309.                 prompter.alert( this.dialog, title, aMessage );
  310.  
  311.                 // Close the dialog.
  312.                 if ( !this.completed ) {
  313.                     this.onCancel();
  314.                 }
  315.             } else {
  316.                 // Error occurred prior to onload even firing.
  317.                 // We can't handle this error until we're done loading, so
  318.                 // defer the handling of this call.
  319.                 this.dialog.setTimeout( function(obj,wp,req,stat,msg){obj.onStatusChange(wp,req,stat,msg)},
  320.                                         100, this, aWebProgress, aRequest, aStatus, aMessage );
  321.             }
  322.         }
  323.     },
  324.  
  325.     // Ignore onLocationChange and onSecurityChange notifications.
  326.     onLocationChange: function( aWebProgress, aRequest, aLocation ) {
  327.     },
  328.  
  329.     onSecurityChange: function( aWebProgress, aRequest, state ) {
  330.     },
  331.  
  332.     // ---------- nsIObserver methods ----------
  333.     observe: function( anObject, aTopic, aData ) {
  334.         // Something of interest occured on the dialog.
  335.         // Dispatch to corresponding implementation method.
  336.         switch ( aTopic ) {
  337.         case "onload":
  338.             this.onLoad();
  339.             break;
  340.         case "oncancel":
  341.             this.onCancel();
  342.             break;
  343.         case "onpause":
  344.             this.onPause();
  345.             break;
  346.         case "onlaunch":
  347.             this.onLaunch();
  348.             break;
  349.         case "onreveal":
  350.             this.onReveal();
  351.             break;
  352.         case "onunload":
  353.             this.onUnload();
  354.             break;
  355.         case "oncompleted":
  356.             // This event comes in when setCompleted needs to be deferred because
  357.             // the dialog isn't loaded yet.
  358.             this.completed = true;
  359.             break;
  360.         default:
  361.             break;
  362.         }
  363.     },
  364.  
  365.     // ---------- nsISupports methods ----------
  366.  
  367.     // This "class" supports nsIProgressDialog, nsIWebProgressListener (by virtue
  368.     // of interface inheritance), nsIObserver, and nsISupports.
  369.     QueryInterface: function (iid) {
  370.         if (iid.equals(Components.interfaces.nsIProgressDialog) ||
  371.             iid.equals(Components.interfaces.nsIDownload) ||
  372.             iid.equals(Components.interfaces.nsITransfer) ||
  373.             iid.equals(Components.interfaces.nsIWebProgressListener) ||
  374.             iid.equals(Components.interfaces.nsIWebProgressListener2) ||
  375.             iid.equals(Components.interfaces.nsIObserver) ||
  376.             iid.equals(Components.interfaces.nsIInterfaceRequestor) ||
  377.             iid.equals(Components.interfaces.nsISupports))
  378.             return this;
  379.  
  380.         Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
  381.         return null;
  382.     },
  383.  
  384.     // ---------- nsIInterfaceRequestor methods ----------
  385.  
  386.     getInterface: function(iid) {
  387.         if (iid.equals(Components.interfaces.nsIPrompt) ||
  388.             iid.equals(Components.interfaces.nsIAuthPrompt)) {
  389.             // use the window watcher service to get a nsIPrompt/nsIAuthPrompt impl
  390.             var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  391.                                .getService(Components.interfaces.nsIWindowWatcher);
  392.             var prompt;
  393.             if (iid.equals(Components.interfaces.nsIPrompt))
  394.                 prompt = ww.getNewPrompter(this.parent);
  395.             else
  396.                 prompt = ww.getNewAuthPrompter(this.parent);
  397.             return prompt;
  398.         }
  399.         Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
  400.         return null;
  401.     },
  402.  
  403.     // ---------- implementation methods ----------
  404.  
  405.     // Initialize the dialog.
  406.     onLoad: function() {
  407.         // Note that onLoad has finished.
  408.         this.loaded = true;
  409.  
  410.         // Fill dialog.
  411.         this.loadDialog();
  412.  
  413.         // Position dialog.
  414.         if ( this.dialog.opener ) {
  415.             this.dialog.moveToAlertPosition();
  416.         } else {
  417.             this.dialog.centerWindowOnScreen();
  418.         }
  419.  
  420.         // Set initial focus on "keep open" box.  If that box is hidden, or, if
  421.         // the download is already complete, then focus is on the cancel/close
  422.         // button.  The download may be complete if it was really short and the
  423.         // dialog took longer to open than to download the data.
  424.         if ( !this.completed && !this.saving ) {
  425.             this.dialogElement( "keep" ).focus();
  426.         } else {
  427.             this.dialogElement( "cancel" ).focus();
  428.         }
  429.     },
  430.  
  431.     // load dialog with initial contents
  432.     loadDialog: function() {
  433.         // Check whether we're saving versus opening with a helper app.
  434.         if ( !this.saving ) {
  435.             // Put proper label on source field.
  436.             this.setValue( "sourceLabel", this.getString( "openingSource" ) );
  437.  
  438.             // Target is the "preferred" application.  Hide if empty.
  439.             if ( this.MIMEInfo && this.MIMEInfo.preferredApplicationHandler ) {
  440.                 var appName = this.MIMEInfo.preferredApplicationHandler.leafName;
  441.                 if ( appName == null || appName.length == 0 ) {
  442.                     this.hide( "targetRow" );
  443.                 } else {
  444.                     // Use the "with:" label.
  445.                     this.setValue( "targetLabel", this.getString( "openingTarget" ) );
  446.                     // Name of application.
  447.                     this.setValue( "target", appName );
  448.                 }
  449.            } else {
  450.                this.hide( "targetRow" );
  451.            }
  452.         } else {
  453.             // If target is not a local file, then hide extra dialog controls.
  454.             if (this.targetFile != null) {
  455.                 this.setValue( "target", this.targetFile.path );
  456.             } else {
  457.                 this.setValue( "target", this.target.spec );
  458.                 this.hide( "pauseResume" );
  459.                 this.hide( "launch" );
  460.                 this.hide( "reveal" );
  461.             }
  462.         }
  463.  
  464.         // Set source field.
  465.         this.setValue( "source", this.source.spec );
  466.  
  467.         var now = ( new Date() ).getTime();
  468.  
  469.         // Intialize the elapsed time.
  470.         if ( !this.elapsed ) {
  471.             this.elapsed = now - this.startTime;
  472.         }
  473.  
  474.         // Update elapsed time display.
  475.         this.setValue( "timeElapsed", this.formatSeconds( this.elapsed / 1000 ) );
  476.         this.setValue( "timeLeft", this.getString( "unknownTime" ) );
  477.  
  478.         // Initialize the "keep open" box.  Hide this if we're opening a helper app
  479.         // or if we are uploading.
  480.         if ( !this.saving || !this.targetFile ) {
  481.             // Hide this in this case.
  482.             this.hide( "keep" );
  483.         } else {
  484.             // Initialize using last-set value from prefs.
  485.             var prefs = Components.classes[ "@mozilla.org/preferences-service;1" ]
  486.                             .getService( Components.interfaces.nsIPrefBranch );
  487.             if ( prefs ) {
  488.                 this.dialogElement( "keep" ).checked = prefs.getBoolPref( "browser.download.progressDnldDialog.keepAlive" );
  489.             }
  490.         }
  491.  
  492.         // Initialize title.
  493.         this.setTitle();
  494.     },
  495.  
  496.     // Cancel button stops the download (if not completed),
  497.     // and closes the dialog.
  498.     onCancel: function() {
  499.         // Cancel the download, if not completed.
  500.         if ( !this.completed ) {
  501.             if ( this.operation ) {
  502.                 const NS_BINDING_ABORTED = 0x804b0002;
  503.                 this.operation.cancel(NS_BINDING_ABORTED);
  504.                 // XXX We're supposed to clean up files/directories.
  505.             }
  506.             if ( this.observer ) {
  507.                 this.observer.observe( this, "oncancel", "" );
  508.             }
  509.             this.paused = false;
  510.         }
  511.         // Test whether the dialog is already closed.
  512.         // This will be the case if we've come through onUnload.
  513.         if ( this.dialog ) {
  514.             // Close the dialog.
  515.             this.dialog.close();
  516.         }
  517.     },
  518.  
  519.     // onunload event means the dialog has closed.
  520.     // We go through our onCancel logic to stop the download if still in progress.
  521.     onUnload: function() {
  522.         // Remember "keep dialog open" setting, if visible.
  523.         if ( this.saving ) {
  524.             var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  525.                           .getService( Components.interfaces.nsIPrefBranch );
  526.             if ( prefs ) {
  527.                 prefs.setBoolPref( "browser.download.progressDnldDialog.keepAlive", this.dialogElement( "keep" ).checked );
  528.             }
  529.          }
  530.          this.dialog = null; // The dialog is history.
  531.          if ( this.mCancelDownloadOnClose ) {
  532.              this.onCancel();
  533.          }
  534.     },
  535.  
  536.     // onpause event means the user pressed the pause/resume button
  537.     // Toggle the pause/resume state (see the function setPause(), below).i
  538.     onPause: function() {
  539.          this.paused = !this.paused;
  540.     },
  541.  
  542.     // onlaunch event means the user pressed the launch button
  543.     // Invoke the launch method of the target file.
  544.     onLaunch: function() {
  545.          try {
  546.            const kDontAskAgainPref  = "browser.download.progressDnlgDialog.dontAskForLaunch";
  547.            try {
  548.              var pref = Components.classes["@mozilla.org/preferences-service;1"]
  549.                               .getService(Components.interfaces.nsIPrefBranch);
  550.              var dontAskAgain = pref.getBoolPref(kDontAskAgainPref);
  551.            } catch (e) {
  552.              // we need to ask if we're unsure
  553.              dontAskAgain = false;
  554.            }
  555.            if ( !dontAskAgain && this.targetFile.isExecutable() ) {
  556.              try {
  557.                var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  558.                                                .getService( Components.interfaces.nsIPromptService );
  559.              } catch (ex) {
  560.                // getService doesn't return null, it throws
  561.                return;
  562.              }
  563.              var title = this.getProperty( "openingAlertTitle",
  564.                                            [ this.fileName() ],
  565.                                            1 );
  566.              var msg = this.getProperty( "securityAlertMsg",
  567.                                          [ this.fileName() ],
  568.                                          1 );
  569.              var dontaskmsg = this.getProperty( "dontAskAgain",
  570.                                                 [ ], 0 );
  571.              var checkbox = {value:0};
  572.              var okToProceed = promptService.confirmCheck(this.dialog, title, msg, dontaskmsg, checkbox);
  573.              try {
  574.                if (checkbox.value != dontAskAgain)
  575.                  pref.setBoolPref(kDontAskAgainPref, checkbox.value);
  576.              } catch (ex) {}
  577.  
  578.              if ( !okToProceed )
  579.                return;
  580.            }
  581.            this.targetFile.launch();
  582.            this.dialog.close();
  583.          } catch ( exception ) {
  584.              // XXX Need code here to tell user the launch failed!
  585.              dump( "nsProgressDialog::onLaunch failed: " + exception + "\n" );
  586.          }
  587.     },
  588.  
  589.     // onreveal event means the user pressed the "reveal location" button
  590.     // Invoke the reveal method of the target file.
  591.     onReveal: function() {
  592.          try {
  593.              this.targetFile.reveal();
  594.              this.dialog.close();
  595.          } catch ( exception ) {
  596.          }
  597.     },
  598.  
  599.     // Get filename from the target.
  600.     fileName: function() {
  601.         if ( this.targetFile != null )
  602.             return this.targetFile.leafName;
  603.         try {
  604.             var escapedFileName = this.target.QueryInterface(nsIURL).fileName;
  605.             var textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
  606.                                          .getService(nsITextToSubURI);
  607.             return textToSubURI.unEscapeURIForUI(this.target.originCharset, escapedFileName);
  608.         } catch (e) {}
  609.         return "";
  610.     },
  611.  
  612.     // Set the dialog title.
  613.     setTitle: function() {
  614.         // Start with saving/opening template.
  615.         // If percentage is not known (-1), use alternate template
  616.         var title = this.saving
  617.             ? ( this.percent != -1 ? this.getString( "savingTitle" ) : this.getString( "unknownSavingTitle" ) )
  618.             : ( this.percent != -1 ? this.getString( "openingTitle" ) : this.getString( "unknownOpeningTitle" ) );
  619.  
  620.  
  621.         // Use file name as insert 1.
  622.         title = this.replaceInsert( title, 1, this.fileName() );
  623.  
  624.         // Use percentage as insert 2 (if known).
  625.         if ( this.percent != -1 ) {
  626.             title = this.replaceInsert( title, 2, this.percent );
  627.         }
  628.  
  629.         // Set dialog's title property.
  630.         if ( this.dialog ) {
  631.             this.dialog.document.title = title;
  632.         }
  633.     },
  634.  
  635.     // Update the dialog to indicate specified percent complete.
  636.     setPercent: function( percent ) {
  637.         // Maximum percentage is 100.
  638.         if ( percent > 100 ) {
  639.             percent = 100;
  640.         }
  641.         // Test if percentage is changing.
  642.         if ( this.percent != percent ) {
  643.             this.mPercent = percent;
  644.  
  645.             // If dialog not opened yet, bail early.
  646.             if ( !this.loaded ) {
  647.                 return this.mPercent;
  648.             }
  649.  
  650.             if ( percent == -1 ) {
  651.                 // Progress meter needs to be in "undetermined" mode.
  652.                 this.mode = "undetermined";
  653.  
  654.                 // Update progress meter percentage text.
  655.                 this.setValue( "progressText", "" );
  656.             } else {
  657.                 // Progress meter needs to be in normal mode.
  658.                 this.mode = "normal";
  659.  
  660.                 // Set progress meter thermometer.
  661.                 this.setValue( "progress", percent );
  662.  
  663.                 // Update progress meter percentage text.
  664.                 this.setValue( "progressText", this.replaceInsert( this.getString( "percentMsg" ), 1, percent ) );
  665.             }
  666.  
  667.             // Update title.
  668.             this.setTitle();
  669.         }
  670.         return this.mPercent;
  671.     },
  672.  
  673.     // Update download rate and dialog display.
  674.     // Note that we don't want the displayed value to quiver
  675.     // between essentially identical values (e.g., 99.9Kb and
  676.     // 100.0Kb) so we only update if we see a big change.
  677.     setRate: function( rate ) {
  678.         if ( rate ) {
  679.             // rate is bytes/sec
  680.             var change = Math.abs( this.rate - rate );
  681.             // Don't update too often!
  682.             if ( change > this.rate / 10 ) {
  683.                 // Displayed rate changes.
  684.                 this.mRate = rate;
  685.             }
  686.         }
  687.         return this.mRate;
  688.     },
  689.  
  690.     // Handle download completion.
  691.     setCompleted: function() {
  692.         // If dialog hasn't loaded yet, defer this.
  693.         if ( !this.loaded ) {
  694.             this.dialog.setTimeout( function(obj){obj.setCompleted()}, 100, this );
  695.             return false;
  696.         }
  697.         if ( !this.mCompleted ) {
  698.             this.mCompleted = true;
  699.  
  700.             // If the "keep dialog open" box is checked, then update dialog.
  701.             if ( this.dialog && this.dialogElement( "keep" ).checked ) {
  702.                 // Indicate completion in status area.
  703.                 var string = this.getString( "completeMsg" );
  704.                 string = this.replaceInsert( string,
  705.                                              1,
  706.                                              this.formatSeconds( this.elapsed/1000 ) );
  707.                 string = this.replaceInsert( string,
  708.                                              2,
  709.                                              this.targetFile.fileSize >> 10 );
  710.  
  711.                 this.setValue( "status", string);
  712.                 // Put progress meter at 100%.
  713.                 this.percent = 100;
  714.  
  715.                 // Set time remaining to 00:00.
  716.                 this.setValue( "timeLeft", this.formatSeconds( 0 ) );
  717.  
  718.                 // Change Cancel button to Close, and give it focus.
  719.                 var cancelButton = this.dialogElement( "cancel" );
  720.                 cancelButton.label = this.getString( "close" );
  721.                 cancelButton.focus();
  722.  
  723.                 // Activate reveal/launch buttons if we enable them.
  724.                 var enableButtons = true;
  725.                 try {
  726.                   var prefs = Components.classes[ "@mozilla.org/preferences-service;1" ]
  727.                                   .getService( Components.interfaces.nsIPrefBranch );
  728.                   enableButtons = prefs.getBoolPref( "browser.download.progressDnldDialog.enable_launch_reveal_buttons" );
  729.                 } catch ( e ) {
  730.                 }
  731.  
  732.                 if ( enableButtons ) {
  733.                     this.enable( "reveal" );
  734.                     try {
  735.                         if ( this.targetFile != null ) {
  736.                             this.enable( "launch" );
  737.                         }
  738.                     } catch(e) {
  739.                     }
  740.                 }
  741.  
  742.                 // Disable the Pause/Resume buttons.
  743.                 this.dialogElement( "pauseResume" ).disabled = true;
  744.  
  745.                 // Fix up dialog layout (which gets messed up sometimes).
  746.                 this.dialog.sizeToContent();
  747.  
  748.                 // GetAttention to show the user that we're done
  749.                this.dialog.getAttention();
  750.             } else if ( this.dialog ) {
  751.                 this.dialog.close();
  752.             }
  753.         }
  754.         return this.mCompleted;
  755.     },
  756.  
  757.     // Set progress meter to given mode ("normal" or "undetermined").
  758.     setMode: function( newMode ) {
  759.         if ( this.mode != newMode ) {
  760.             // Need to update progress meter.
  761.             this.dialogElement( "progress" ).setAttribute( "mode", newMode );
  762.         }
  763.         return this.mMode = newMode;
  764.     },
  765.  
  766.     // Set pause/resume state.
  767.     setPaused: function( pausing ) {
  768.         // If state changing, then update stuff.
  769.         if ( this.paused != pausing ) {
  770.             var string = pausing ? "resume" : "pause";
  771.             this.dialogElement( "pauseResume" ).label = this.getString(string);
  772.  
  773.             // If we have an observer, tell it to suspend/resume
  774.             if ( this.observer ) {
  775.                 this.observer.observe( this, pausing ? "onpause" : "onresume" , "" );
  776.             }
  777.         }
  778.         return this.mPaused = pausing;
  779.     },
  780.  
  781.     // Convert raw rate (bytes/sec) to Kbytes/sec (to nearest tenth).
  782.     rateToKRate: function( rate ) {
  783.         return ( rate / 1024 ).toFixed(1);
  784.     },
  785.  
  786.     // Format number of seconds in hh:mm:ss form.
  787.     formatSeconds: function( secs ) {
  788.         // Round the number of seconds to remove fractions.
  789.         secs = parseInt( secs + .5 );
  790.         var hours = parseInt( secs/3600 );
  791.         secs -= hours*3600;
  792.         var mins = parseInt( secs/60 );
  793.         secs -= mins*60;
  794.         var result;
  795.         if ( hours )
  796.             result = this.getString( "longTimeFormat" );
  797.         else
  798.             result = this.getString( "shortTimeFormat" );
  799.  
  800.         if ( hours < 10 )
  801.             hours = "0" + hours;
  802.         if ( mins < 10 )
  803.             mins = "0" + mins;
  804.         if ( secs < 10 )
  805.             secs = "0" + secs;
  806.  
  807.         // Insert hours, minutes, and seconds into result string.
  808.         result = this.replaceInsert( result, 1, hours );
  809.         result = this.replaceInsert( result, 2, mins );
  810.         result = this.replaceInsert( result, 3, secs );
  811.  
  812.         return result;
  813.     },
  814.  
  815.     // Get dialog element using argument as id.
  816.     dialogElement: function( id ) {
  817.         // Check if we've already fetched it.
  818.         if ( !( id in this.fields ) ) {
  819.             // No, then get it from dialog.
  820.             try {
  821.                 this.fields[ id ] = this.dialog.document.getElementById( id );
  822.             } catch(e) {
  823.                 this.fields[ id ] = {
  824.                     value: "",
  825.                     setAttribute: function(id,val) {},
  826.                     removeAttribute: function(id) {}
  827.                     }
  828.             }
  829.         }
  830.         return this.fields[ id ];
  831.     },
  832.  
  833.     // Set dialog element value for given dialog element.
  834.     setValue: function( id, val ) {
  835.         this.dialogElement( id ).value = val;
  836.     },
  837.  
  838.     // Enable dialgo element.
  839.     enable: function( field ) {
  840.         this.dialogElement( field ).removeAttribute( "disabled" );
  841.     },
  842.  
  843.     // Get localizable string from properties file.
  844.     getProperty: function( propertyId, strings, len ) {
  845.         if ( !this.mBundle ) {
  846.             this.mBundle = Components.classes[ "@mozilla.org/intl/stringbundle;1" ]
  847.                              .getService( Components.interfaces.nsIStringBundleService )
  848.                                .createBundle( "chrome://global/locale/nsProgressDialog.properties");
  849.         }
  850.         return this.mBundle.formatStringFromName( propertyId, strings, len );
  851.     },
  852.  
  853.     // Get localizable string (from dialog <data> elements).
  854.     getString: function ( stringId ) {
  855.         // Check if we've fetched this string already.
  856.         if ( !( this.strings && stringId in this.strings ) ) {
  857.             // Presume the string is empty if we can't get it.
  858.             this.strings[ stringId ] = "";
  859.             // Try to get it.
  860.             try {
  861.                 this.strings[ stringId ] = this.dialog.document.getElementById( "string."+stringId ).childNodes[0].nodeValue;
  862.             } catch (e) {}
  863.        }
  864.        return this.strings[ stringId ];
  865.     },
  866.  
  867.     // Replaces insert ("#n") with input text.
  868.     replaceInsert: function( text, index, value ) {
  869.         var result = text;
  870.         var regExp = new RegExp( "#"+index );
  871.         result = result.replace( regExp, value );
  872.         return result;
  873.     },
  874.  
  875.     // Hide a given dialog field.
  876.     hide: function( field ) {
  877.         this.dialogElement( field ).hidden = true;
  878.  
  879.         // Also hide any related separator element...
  880.         var sep = this.dialogElement( field+"Separator" );
  881.         if (sep)
  882.             sep.hidden = true;
  883.     },
  884.  
  885.     // Return input in hex, prepended with "0x" and leading zeros (to 8 digits).
  886.     hex: function( x ) {
  887.         return "0x" + ("0000000" + Number(x).toString(16)).slice(-8);
  888.     },
  889.  
  890.     // Dump text (if debug is on).
  891.     dump: function( text ) {
  892.         if ( this.debug ) {
  893.             dump( text );
  894.         }
  895.     }
  896. }
  897.  
  898. // This Component's module implementation.  All the code below is used to get this
  899. // component registered and accessible via XPCOM.
  900. var module = {
  901.     // registerSelf: Register this component.
  902.     registerSelf: function (compMgr, fileSpec, location, type) {
  903.         var compReg = compMgr.QueryInterface( Components.interfaces.nsIComponentRegistrar );
  904.         compReg.registerFactoryLocation( this.cid,
  905.                                          "Mozilla Download Progress Dialog",
  906.                                          this.contractId,
  907.                                          fileSpec,
  908.                                          location,
  909.                                          type );
  910.     },
  911.  
  912.     // getClassObject: Return this component's factory object.
  913.     getClassObject: function (compMgr, cid, iid) {
  914.         if (!cid.equals(this.cid))
  915.             throw Components.results.NS_ERROR_NO_INTERFACE;
  916.  
  917.         if (!iid.equals(Components.interfaces.nsIFactory))
  918.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  919.  
  920.         return this.factory;
  921.     },
  922.  
  923.     /* CID for this class */
  924.     cid: Components.ID("{F5D248FD-024C-4f30-B208-F3003B85BC92}"),
  925.  
  926.     /* Contract ID for this class */
  927.     contractId: "@mozilla.org/progressdialog;1",
  928.  
  929.     /* factory object */
  930.     factory: {
  931.         // createInstance: Return a new nsProgressDialog object.
  932.         createInstance: function (outer, iid) {
  933.             if (outer != null)
  934.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  935.  
  936.             return (new nsProgressDialog()).QueryInterface(iid);
  937.         }
  938.     },
  939.  
  940.     // canUnload: n/a (returns true)
  941.     canUnload: function(compMgr) {
  942.         return true;
  943.     }
  944. };
  945.  
  946. // NSGetModule: Return the nsIModule object.
  947. function NSGetModule(compMgr, fileSpec) {
  948.     return module;
  949. }
  950.